using System;
using System.Collections.Generic;
using FireStarter.Base;
using FireStarter.CodeGeneration;
using FireStarter.CodeGeneration.Generator;
using FireStarterModeller.BO;
using NUnit.Framework;

namespace FireStarter.Test.CodeGeneration.Generator
{
    [TestFixture]
    public class TestCodeGenInterpreter
    {

        //TODO: Implement an 'Execute' Action type that sends it's text to the strategy as an action.

        [Test]
        public void TestStringLiteral()
        {
            //---------------Set up test pack-------------------
            string stringText = TestUtilsCodeGen.GetRandomString();
            LiteralExpression literalExpression = new LiteralExpression(stringText);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            string output = literalExpression.Generate(null, null);
            //---------------Test Result -----------------------
            Assert.AreEqual(output, stringText);
        }

        [Test]
        public void TestValueReplacement()
        {
            //---------------Set up test pack-------------------
            const string projectname = "ProjectName";
            ValueExpression expression = new ValueExpression(projectname);

            GenerationStrategyStub strategy = new GenerationStrategyStub();
            string expectedValue = TestUtilsCodeGen.GetRandomString();
            strategy.SetValue(projectname, expectedValue);

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelSolutionStub), strategy);

            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            string output = expression.Generate(modelStrategyMapper, new ModelSolutionStub());

            //---------------Test Result -----------------------
            Assert.AreEqual(expectedValue, output);
        }


        [Test]
        public void TestTemplateExpression()
        {
            //---------------Set up test pack-------------------
            ValueExpression valueExpression = new ValueExpression("BOClassName");
            LiteralExpression literalExpression = new LiteralExpression(" = ");
            //---------------Assert Precondition----------------
            Assert.IsNull(valueExpression.Next);
            Assert.IsNull(literalExpression.Next);
            //---------------Execute Test ----------------------
            valueExpression.Next = literalExpression;
            //---------------Test Result -----------------------
            Assert.AreSame(literalExpression, valueExpression.Next);
            Assert.IsNull(literalExpression.Next);
        }

        [Test]
        public void TestListOfExpressions_2Elements()
        {
            //---------------Set up test pack-------------------
            //<<|=BOClassName|>> = 
            const string boclassname = "BOClassName";
            ValueExpression valueExpression = new ValueExpression(boclassname);
            const string literal = " = ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            valueExpression.Next = literalExpression;

            GenerationStrategyStub strategy = new GenerationStrategyStub();
            string expectedValue = TestUtilsCodeGen.GetRandomString();
            strategy.SetValue(boclassname, expectedValue);

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategy);

            //---------------Execute Test ----------------------
            string output = valueExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedValue + literal, output);
        }

        [Test]
        public void TestListOfExpressions_6Elements()
        {
            //---------------Set up test pack-------------------
            //<<|=BOClassName|>> <<|=BOVariableName|>> = new <<|=BOClassName|>>();
            const string boclassname = "BOClassName";
            const string literal1 = " ";
            const string bovariablename = "BOVariableName";
            const string literal2 = " = new  ";
            const string literal3 = "();";

            ValueExpression valueExpression1 = new ValueExpression(boclassname);
            LiteralExpression literalExpression1 = new LiteralExpression(literal1);
            valueExpression1.Next = literalExpression1;
            ValueExpression valueExpression2 = new ValueExpression(bovariablename);
            literalExpression1.Next = valueExpression2;
            LiteralExpression literalExpression2 = new LiteralExpression(literal2);
            valueExpression2.Next = literalExpression2;
            ValueExpression valueExpression3 = new ValueExpression(boclassname);
            literalExpression2.Next = valueExpression3;
            LiteralExpression literalExpression3 = new LiteralExpression(literal3);
            valueExpression3.Next = literalExpression3;

            GenerationStrategyStub strategy = new GenerationStrategyStub();
            string classNameExpValue = TestUtilsCodeGen.GetRandomString();
            string bovariableNameExpValue = TestUtilsCodeGen.GetRandomString();
            strategy.SetValue(boclassname, classNameExpValue);
            strategy.SetValue(bovariablename, bovariableNameExpValue);

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategy);

            //---------------Execute Test ----------------------
            string output = valueExpression1.Generate(modelStrategyMapper, new ModelElementStub());

            //---------------Test Result -----------------------
            Assert.AreEqual(classNameExpValue + literal1 + bovariableNameExpValue + literal2 + classNameExpValue + literal3, output);
        }

        
        

        //TODO: error message when your factory doesn't have a strategy for a type

        //<<|FOREACH(IModelPropDefs):
        //		<<|=PropName|>> = <<|=ValidValue|>>;
        //|>>

        //GenerationStrategyStub strategy = new GenerationStrategyStub();
        //strategy.SetValue(imodelpropdefs);
        /*
                      string propname = "PropName";
                    string literal = " = ";
                    string validvalue = "ValidValue";
                    ValueExpression valueExpression1 = new ValueExpression(propname);
                    LiteralExpression literalExpression1 = new LiteralExpression(literal);
                    valueExpression1.Next = literalExpression1;
                    ValueExpression valueExpression2 = new ValueExpression(validvalue);
                    literalExpression1.Next = valueExpression2;
        */
    }

    internal class ModelSolutionStub : IModelSolution
    {
        public IModelElement Parent
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }

        public IList<IModelClass> ModelClasses
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string BOProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string LogicProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string BOTestProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string UIProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string UIVWGProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string UIWinProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public IList<IModelAssembly> ModelAssemblies
        {
            get { throw new System.NotImplementedException(); }
        }

        public string ApplicationName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string ProjectName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }
    }

    internal class ModelElementStub : IModelElement
    {
        public string StubValue { get; set; }

        public IModelElement Parent { get; set; }
    }

    internal class ModelPropertyStub : IModelProperty
    {
        public ModelPropertyStub(string propertyName)
        {
            PropertyName = propertyName;
        }

        public string PropertyName { get; set; }

        public IModelPropertyType PropertyType
        {
            get { return null; }
            set { throw new NotImplementedException(); }
        }

        public string PropertyTypeString
        {
            get { throw new System.NotImplementedException(); }
        }

        public bool HasSetter
        {
            get { return false; }
            set { throw new NotImplementedException(); }
        }

        public string DefaultValue { get; set; }

        public bool IsCompulsory
        {
            get { throw new System.NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }

        public string ValidValue
        {
            get { throw new System.NotImplementedException(); }
        }

        public string Max
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string Min
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public ModelLookupType LookupListType
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string DisplayName
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string ReadWriteRule
        {
            get { throw new System.NotImplementedException(); }
            set { throw new System.NotImplementedException(); }
        }

        public string DatabaseField
        {
            get { throw new NotImplementedException(); } }

        public string Description
        {
            get { throw new NotImplementedException(); } }

        public bool AutoIncrementing
        {
            get { throw new NotImplementedException(); } }

        public bool HasLength
        {
            get { throw new NotImplementedException(); } }

        public int Length
        {
            get { throw new NotImplementedException(); } }

        public bool KeepValuePrivate
        {
            get { throw new NotImplementedException(); } }

        public bool GenerateCode
        {
            get { throw new NotImplementedException(); }
        }

        public IModelElement Parent
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }
    }

    internal class GenerationStrategyStub : IGenerationStrategy
    {
        private readonly IDictionary<string, string> _values = new Dictionary<string, string>( );
        private readonly IDictionary<string, IList<IModelElement>> _modelElementLists = new Dictionary<string, IList<IModelElement>>();

        public object GetValue(IModelStrategyMapper modelStrategyMapper, IModelElement modelElement, string propertyName)
        {
            ModelElement = modelElement; //needed to make test pass, why?  how do we know it will get implemented in the real children?
            return _values[propertyName];
        }

        public void SetValue(string propertyName, string value)
        {
            _values[propertyName] = value;
        }

        public IEnumerable<IModelElement> GetCollection(IModelStrategyMapper modelStrategyMapper, IModelElement modelElement, string collectionName)
        {
            ModelElement = modelElement; //needed to make test pass, why?  how do we know it will get implemented in the real children?
            return _modelElementLists[collectionName];
        }

        public void SetCollection(string collectionName, IList<IModelElement> collection)
        {
            _modelElementLists[collectionName] = collection;
        }

        public IModelElement ModelElement { get; private set; }
    }

    internal class GenerationPropertyStrategyStub : IGenerationStrategy
    {
        public object GetValue(IModelStrategyMapper modelStrategyMapper, IModelElement modelElement, string propertyName)
        {
            if (propertyName == "PropName") return ((IModelProperty)modelElement).PropertyName;
            throw new Exception("property not found:" + propertyName);
        }

        public IEnumerable<IModelElement> GetCollection(IModelStrategyMapper modelStrategyMapper, IModelElement modelElement, string collectionName)
        {
            throw new NotImplementedException();
        }
    }
}